除了一對多的關聯方式以外,資料和資料間也有可能是多對多的關聯方式。
比方說常見的用戶標籤(tag) 系統,就是一種多對多的關聯:每個標籤可能對應多個用戶,而每個用戶也可能被標記上多個不同標籤。
要怎麼設計這樣的關聯呢?我們一起來看看
多對多關係的處理方式,比較建議的做法是設計一個中間表,每筆資料紀錄
比方說,如果我們的資料是
user |
tag |
---|---|
Alice | admin, registered, author |
Bob | customer, unregistered |
Carol | customer, registered, reader |
那我們的資料表內容可能會是
|user
|
|---|---|
|id
|name
|
|1|Alice|
|2|Bob|
|3|Carol|
|tag
|
|---|---|
|id
|name
|
|1|admin|
|2|registered|
|3|author|
|4|customer|
|5|unregistered|
|6|reader|
|user_tag
|
|---|---|
|user_id
|tag_id
|
|1|1|
|1|2|
|1|3|
|2|4|
|2|5|
|3|4|
|3|2|
|3|6|
假設我們需要的用戶標籤系統,tag
資料表和 DAO 物件如下
object Tags : IntIdTable() {
val name = varchar("name", 50)
}
class Tag(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<Tag>(Tags)
var name by Tags.name
}
我們定義中間表 user_tag
如下
object UserTag : IntIdTable() {
val user = reference("user", Users)
val tag = reference("tag", Tags)
}
利用 via
這個內綴,我們讓 user
和 tag
物件與 UserTag
資料表進行關聯
class Tag(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<Tag>(Tags)
var name by Tags.name
var users by User via UsersTags
}
class User(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<User>(Users)
var name by Users.name
var tags by Tag via UsersTags
}
並且定義 user
和 tag
內容
SchemaUtils.create(Users)
SchemaUtils.create(Tags)
val alice = User.new {
name = "Alice"
}
val bob = User.new {
name = "Bob"
}
val carol = User.new {
name = "Carol"
}
val admin = Tag.new {
name = "admin"
}
val registered = Tag.new {
name = "registered"
}
val author = Tag.new {
name = "author"
}
val customer = Tag.new {
name = "customer"
}
val unregistered = Tag.new {
name = "unregistered"
}
val reader = Tag.new {
name = "reader"
}
定義完成之後,我們就可以以 DAO 的方式,進行 user
和 tag
的關聯了
alice.tags = SizedCollection(listOf(admin, registered, author))
bob.tags = SizedCollection(listOf(customer, unregistered))
carol.tags = SizedCollection(listOf(customer, registered, reader))
定義完成之後,我們可以透過 user
來找到所有對應的 tag
carol.tags.forEach { println("tag: ${it.name}") }
上面這段程式碼會印出
tag: customer
tag: registered
tag: reader
我們也可以透過 tag
來找到對應的 user
registered.users.forEach { println("user: ${it.name}") }
上面這段程式碼會印出
user: Alice
user: Carol
透過 DAO 的方式,以物件的思維,來實作多對多的資料關聯語法,個人認為比起透過 query 關聯的方式來說,更加直觀並且好操作許多。